package app;

import gui.designComponents.TMTCheckBox;
import gui.designComponents.TMTComboBox;
import gui.designComponents.TMTTable;
import gui.specialGUIs.EventSinglePanel;
import gui.specialGUIs.LoginPanel;
import gui.specialGUIs.MemberSinglePanel;
import gui.specialGUIs.OverviewPanel;
import gui.specialGUIs.ParticipantsPanel;
import gui.specialGUIs.StatsPanel;
import gui.specialGUIs.TrainingPlanPanel;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.ResourceBundle;

import javax.swing.JOptionPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.TableColumnModel;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import util.DateFormatting;
import util.PasswordFunctions;
import util.StringStringPair;
import util.ValueStringPair;
import bundles.BundleLoader;
import definitions.Commands;
import definitions.Constants;
import entities.Address;
import entities.Event;
import entities.Member;
import entities.User;

/**
 * Central Conntroller of this applications. Catches every Action and MouseEvent
 * fired in this Application. For each Event, it delegates certain functions to
 * model and view.
 * 
 * @author MK
 * 
 */
public class Controller extends MouseAdapter implements ActionListener,
		ChangeListener {

	private Logger logger;
	private ResourceBundle resBundle;

	private Model model;
	private View view;

	private Controller() {

		DOMConfigurator.configureAndWatch("logs/log_config.xml",
				60 * 1000);
		logger = Logger.getRootLogger();
		this.resBundle = BundleLoader.getResourceBundle();
	}

	public Controller(Model model) {
		this();
		this.model = model;
	}

	public void setView(View view) {
		this.view = view;
	}

	@Override
	public void actionPerformed(ActionEvent event) {

		String command = event.getActionCommand();
		logger.info("CONTROLLER: " + command);

		if (command == Commands.SHOW_EVENT_OVERVIEW
				|| command == Commands.SHOW_MEMBER_OVERVIEW
				|| command == Commands.SHOW_STATS_OVERVIEW
				|| command == Commands.SHOW_START_VIEW
				|| command == Commands.SHOW_TRAININGPLAN_CREATE
				|| command == Commands.SHOW_TRAININGPLAN_CREATE_WITH_SELECTION
				|| command == Commands.SHOW_PARTICIPANTS
				|| command == Commands.GO_BACK) {
			view.setMainContent(command);
		} else if (command == Commands.DO_LOGIN) {
			doLogin();
		} else if (command == Commands.SHOW_PROFILE) {
			showProfile();
		} else if (command == Commands.DO_LOGOUT) {
			doLogout();
		} else if (command == Commands.SHOW_EVENT_ADDRESS_CHANGE) {
			showEventAddressChange();
		} else if (command == Commands.SHOW_EVENT_CREATE) {
			showEventCreate();
		} else if (command == Commands.SHOW_EVENT_UPDATE) {
			showEventUpdate(null);
		} else if (command == Commands.SHOW_MEMBER_CREATE) {
			showMemberCreate();
		} else if (command == Commands.SHOW_MEMBER_UPDATE) {
			showMemberUpdate(null);
		} else if (command == Commands.SHOW_MEMBER_ACCOUNT) {
			showMemberAccount();
		} else if (command == Commands.MEMBER_UPDATE) {
			memberUpdate();
		} else if (command == Commands.MEMBER_DELETE) {
			memberDelete();
		} else if (command == Commands.PARTICIPANTS_UPDATE) {
			participantsUpdate();
			view.setMainContent(Commands.SHOW_EVENT_SINGLE);
		} else if (command == Commands.EVENT_DELETE) {
			eventDelete();
		} else if (command == Commands.EVENT_UPDATE) {
			eventUpdate();
		} else if (command == Commands.DO_TRAININGPLAN_CREATE) {
			trainingPlanCreate();
		} else if (command == Commands.PARTICIPANTS_UPDATE_LIVE) {
			participantsUpdate();
			view.setMainContent(Commands.SHOW_PARTICIPANTS);
		} else if (command == Commands.PRINT_STAT) {
			printStat();
		}
	}

	public void mouseClicked(MouseEvent me) {

		Component component = me.getComponent();
		String componentName = me.getComponent().getName();

		logger.info("CONTROLLER: MC on " + componentName);

		int clickCount = me.getClickCount();

		if (componentName.equals(Constants.LOGO_PANEL)) {
			view.setMainContent(Commands.SHOW_START_VIEW);
		} else if (clickCount > 1) {
			if (componentName.equals(Commands.SHOW_EVENT_OVERVIEW)
					|| componentName.equals(Commands.SHOW_EVENT_OVERVIEW_NEAR)) {
				showEventUpdate(component);

			} else if (componentName.equals(Commands.SHOW_MEMBER_OVERVIEW)
					|| componentName
							.equals(Commands.SHOW_MEMBER_OVERVIEW_HEALTHY)
					|| componentName
							.equals(Commands.SHOW_MEMBER_OVERVIEW_NOT_HEALTHY)) {
				showMemberUpdate(component);
			} else if (componentName.equals(Commands.SHOW_PARTICIPANTS)) {
				view.setMainContent(Commands.SHOW_PARTICIPANTS);
			}
		}
	}

	@Override
	public void stateChanged(ChangeEvent event) {
		logger.warn("CONTROLLER: Should not happen: " + event);
	}

	private void doLogin() {

		LoginPanel loginPanel = (LoginPanel) view.getMainContent();

		String username = loginPanel.getUserNameField().getText();
		String password = new String(loginPanel.getPasswordField()
				.getPassword());
		String passwordAsMD5 = PasswordFunctions.encodeString(password);

		User foundUser = model.checkLoginData(username, passwordAsMD5);

		if (foundUser == null) {
			String messageContent = resBundle.getString("loginFailedMessage");
			String messageTitle = resBundle.getString("loginFailedTitle");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);

			loginPanel.getPasswordField().setText("");

			logger.warn("CONTROLLER: LoginFailed for user: " + username);
			
		} else {

			logger.info("CONTROLLER: Login Successfull for user: " + username);
			model.setLoggedInUser(foundUser);
			view.showGUIAfterLogIn();
		}
	}

	private void doLogout() {

		String confirmMessage = resBundle.getString("wantToLogout");
		String confirmTitle = resBundle.getString("selectAnOption");

		int decision = JOptionPane.showConfirmDialog(null, confirmMessage,
				confirmTitle, JOptionPane.YES_NO_OPTION);

		if (decision == 0) {

			logger.info("CONTROLLER: Logout Successfull");
			model.setLoggedInUser(null);
			view.showGUIAfterLogOut();
		}
	}

	private void eventDelete() {
		String confirmMessage = resBundle.getString("deleteEvent");
		String confirmTitle = resBundle.getString("selectAnOption");

		int decision = JOptionPane.showConfirmDialog(null, confirmMessage,
				confirmTitle, JOptionPane.YES_NO_OPTION);

		if (decision == 0) {
			logger.warn("CONTROLLER: Event Delete");
			model.deleteEvent();
			view.setMainContent(Commands.SHOW_EVENT_OVERVIEW);
		}
	}

	private void eventUpdate() {

		try {
			EventSinglePanel eventPanel = (EventSinglePanel) view
					.getMainContent();

			String street = eventPanel.getStreetField().getText();
			int housenumber = Integer.valueOf(eventPanel.getHousenumberField()
					.getText());
			int postal = Integer.valueOf(eventPanel.getPostalField().getText());
			String city = eventPanel.getCityField().getText();

			String addressIdentifier = (String) eventPanel
					.getAddressIdentifierBox().getSelectedItem();

			if (addressIdentifier != null
					&& addressIdentifier.equals(resBundle
							.getString("newAddress"))) {
				addressIdentifier = null;
			}

			Address newAddress = new Address(street, Integer
					.valueOf(housenumber), Integer.valueOf(postal), city,
					addressIdentifier);

			ValueStringPair vsp = (ValueStringPair) (eventPanel.getMonthBox()
					.getSelectedItem());

			int month = vsp.getObjValue();
			int year = Integer.valueOf(eventPanel.getYearField().getText());
			String dayAsString = (String) eventPanel.getDayBox()
					.getSelectedItem();
			int day = Integer.valueOf(dayAsString);

			vsp = (ValueStringPair) eventPanel.getTimeBox().getSelectedItem();
			int hour = vsp.getObjValue();

			Long date = new GregorianCalendar(year, month, day, hour, 0)
					.getTimeInMillis();

			if (model.getSelectedEvent() == null) {
				logger.warn("CONTROLLER: Event Create");
				model.createEvent(date, newAddress);

			} else if (model.getSelectedEvent() != null) {
				logger.warn("CONTROLLER: Event Update");
				model.updateEvent(date, newAddress);

			}
			view.setMainContent(Commands.SHOW_EVENT_OVERVIEW);

		} catch (Exception e) {

			logger.error("CONTROLLER: Wrong Input at eventUpdate()");

			String messageContent = resBundle.getString("inputMissing");
			String messageTitle = resBundle.getString("attention");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);
		}
	}

	private void memberDelete() {

		String confirmMessage = resBundle.getString("deletePlayer");
		String confirmTitle = resBundle.getString("selectAnOption");

		int decision = JOptionPane.showConfirmDialog(null, confirmMessage,
				confirmTitle, JOptionPane.YES_NO_OPTION);

		if (decision == 0) {
			model.deleteMember();
			view.setMainContent(Commands.SHOW_MEMBER_OVERVIEW);
			logger.warn("CONTROLLER: Member Delete");
		}
	}

	private void memberUpdate() {

		try {
			MemberSinglePanel memberPanel = (MemberSinglePanel) view
					.getMainContent();

			String street = memberPanel.getStreetField().getText();
			int housenumber = Integer.valueOf(memberPanel.getHousenumberField()
					.getText());
			int postal = Integer
					.valueOf(memberPanel.getPostalField().getText());
			String city = memberPanel.getCityField().getText();

			Address newAddress = new Address(street, Integer
					.valueOf(housenumber), Integer.valueOf(postal), city, null);

			String email = memberPanel.getEmailField().getText();

			String phoneHome = memberPanel.getPhoneHomeField().getText();
			String phoneMobile = memberPanel.getPhoneMobileField().getText();

			StringStringPair ssp = (StringStringPair) memberPanel.getStateBox()
					.getSelectedItem();
			String state = ssp.getObjValue();

			ssp = (StringStringPair) memberPanel.getPositionBox()
					.getSelectedItem();
			String position = ssp.getObjValue();

			ssp = (StringStringPair) memberPanel.getRoleBox().getSelectedItem();
			String role = ssp.getObjValue();

			ValueStringPair vsp = (ValueStringPair) (memberPanel.getMonthBox()
					.getSelectedItem());

			String nameFirst = memberPanel.getNameFirstField().getText();
			String nameLast = memberPanel.getNameLastField().getText();

			int month = vsp.getObjValue();
			int year = Integer.valueOf(memberPanel.getYearField().getText());
			String dayAsString = (String) memberPanel.getDayBox()
					.getSelectedItem();
			int day = Integer.valueOf(dayAsString);

			Long date = new GregorianCalendar(year, month, day)
					.getTimeInMillis();

			if (model.getSelectedMember() == null) {

				if (model.isMemberAlreadyExisting(nameFirst, nameLast)) {

					String messageContent = resBundle
							.getString("memberAlreadyExisting");
					String messageTitle = resBundle.getString("attention");

					JOptionPane.showMessageDialog(null, messageContent,
							messageTitle, JOptionPane.ERROR_MESSAGE);

					logger
							.error("CONTROLLER: memberUpdate - Member already existing ("
									+ nameFirst + " " + nameLast + ")");

					return;
				} else {
					model.createMember(nameFirst, nameLast, date, newAddress,
							email, phoneHome, phoneMobile, state, position,
							role);

					logger.warn("CONTROLLER: Member Create");

					view.setMainContent(Commands.SHOW_MEMBER_OVERVIEW);
				}
			} else if (model.getSelectedMember() != null) {

				logger.warn("CONTROLLER: Member Update");
				model.updateMember(newAddress, email, phoneHome, phoneMobile,
						state, position, role);

			}
			view.setMainContent(Commands.SHOW_MEMBER_OVERVIEW);

		} catch (Exception e) {

			logger.error("CONTROLLER: Member Update - Wrong Input");

			String messageContent = resBundle.getString("inputMissing");
			String messageTitle = resBundle.getString("attention");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);
		}
	}

	private void participantsUpdate() {

		ParticipantsPanel partiPanel = (ParticipantsPanel) view
				.getMainContent();

		ArrayList<TMTCheckBox> checkBoxes = partiPanel.getCheckBoxes();
		ArrayList<TMTComboBox> ratingBoxes = partiPanel.getRatingComboBoxes();

		ArrayList<Integer> selectedMembers = new ArrayList<Integer>();
		ArrayList<Integer> ratings = new ArrayList<Integer>();

		for (int counter = 0; counter < checkBoxes.size(); counter++) {
			TMTCheckBox singleBox = checkBoxes.get(counter);
			if (singleBox.isSelected()) {
				selectedMembers.add(counter);

				ValueStringPair vsp = (ValueStringPair) ratingBoxes
						.get(counter).getSelectedItem();

				ratings.add(vsp.getObjValue());
			}
		}
		model.updateParticipantsForEvent(selectedMembers, ratings);
	}

	private void printStat() {
		StatsPanel statsPanel = (StatsPanel) view.getMainContent();
		statsPanel.printStat();

		logger.info("CONTROLLER: Print Statistic");
	}

	private void showEventAddressChange() {
		EventSinglePanel eventPanel = (EventSinglePanel) view.getMainContent();

		String addressIdentifier = (String) eventPanel
				.getAddressIdentifierBox().getSelectedItem();

		model.getAddressFromIdentifier(addressIdentifier);
		view.setMainContent(Commands.SHOW_EVENT_ADDRESS_CHANGE);

		logger.info("CONTROLLER: showEventAddressChange");
	}

	private void showEventCreate() {
		model.setSelectedEvent(null);
		view.setMainContent(Commands.SHOW_EVENT_SINGLE);

		logger.info("CONTROLLER: showEventCreate");
	}

	private void showEventUpdate(Component component) {

		TMTTable table;

		if (component == null) {
			OverviewPanel op = (OverviewPanel) view.getMainContent();
			table = op.getTablePanel().getTMTTable();
		} else {

			table = (TMTTable) component;
		}

		TableColumnModel columnModel = table.getTableHeader().getColumnModel();
		int selectedRow = table.getSelectedRow();

		if (selectedRow == -1) {

			String messageContent = resBundle.getString("noEventSelected");
			String messageTitle = resBundle.getString("attention");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);

			logger
					.error("CONTROLLER: showEventUpdate - Error: No Event Selected");

		} else {
			String dateAsString = (String) table.getValueAt(selectedRow,
					columnModel.getColumnIndex(resBundle.getString("date")));

			DateFormat df = DateFormatting.DATE_TIME_INSTANCE;

			try {
				Date date = df.parse(dateAsString);
				Event existingEvent = model.searchEvent(date);

				model.setSelectedEvent(existingEvent);
				view.setMainContent(Commands.SHOW_EVENT_SINGLE);

				logger.info("CONTROLLER: showEventUpdate + (" + existingEvent
						+ ")");

			} catch (ParseException e) {
				e.printStackTrace();
			}
		}
	}

	private void showMemberAccount() {

		String dialogTitle = resBundle.getString("notImplemented");
		String messageString = resBundle.getString("notYetImplemented");
		JOptionPane.showMessageDialog(null, messageString, dialogTitle,
				JOptionPane.INFORMATION_MESSAGE);

		logger.info("CONTROLLER: showMemberAccount - Not Implemented");
	}

	private void showMemberCreate() {
		model.setSelectedMember(null);
		view.setMainContent(Commands.SHOW_MEMBER_SINGLE);
		logger.info("CONTROLLER: showMemberCreate");
	}

	private void showMemberUpdate(Component component) {

		TMTTable table;
		if (component == null) {
			OverviewPanel op = (OverviewPanel) view.getMainContent();
			table = op.getTablePanel().getTMTTable();
		} else {

			table = (TMTTable) component;
		}

		TableColumnModel columnModel = table.getTableHeader().getColumnModel();
		int selectedRow = table.getSelectedRow();

		if (selectedRow == -1) {

			String messageContent = resBundle.getString("noMemberSelected");
			String messageTitle = resBundle.getString("attention");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);

			logger.error("CONTROLLER: showMemberUpdate - No Member Selected");

		} else {
			String nameFirst = (String) table.getValueAt(selectedRow,
					columnModel
							.getColumnIndex(resBundle.getString("nameFirst")));
			String nameLast = (String) table
					.getValueAt(selectedRow, columnModel
							.getColumnIndex(resBundle.getString("nameLast")));

			Member existingMember = model.searchMember(nameFirst, nameLast);
			model.setSelectedMember(existingMember);
			view.setMainContent(Commands.SHOW_MEMBER_SINGLE);

			logger.info("CONTROLLER: showMemberUpdate");
		}
	}

	private void showProfile() {

		model.setSelectedMember(model.getLoggedInUser().getMember());
		view.setMainContent(Commands.SHOW_PROFILE);

		logger.info("CONTROLLER: showProfile");
	}

	private void trainingPlanCreate() {

		TrainingPlanPanel tpp = (TrainingPlanPanel) view.getMainContent();

		int trainingDays = tpp.numberOfTrainings;

		if (trainingDays == 0) {

			String messageContent = resBundle
					.getString("noTrainingPlanCreated");
			String messageTitle = resBundle.getString("attention");

			JOptionPane.showMessageDialog(null, messageContent, messageTitle,
					JOptionPane.ERROR_MESSAGE);

			logger.error("CONTROLLER: trainingPlanCreate - No Days selected");

		} else {

			try {

				ValueStringPair vsp = (ValueStringPair) tpp.seasonStartMonthBox
						.getSelectedItem();
				int startMonth = vsp.getObjValue();
				String dayAsString = (String) tpp.seasonStartDayBox
						.getSelectedItem();
				int startDay = Integer.valueOf(dayAsString);
				int startYear = Integer.valueOf(tpp.seasonStartYearField
						.getText());

				vsp = (ValueStringPair) tpp.seasonEndMonthBox.getSelectedItem();
				int endMonth = vsp.getObjValue();
				dayAsString = (String) tpp.seasonEndDayBox.getSelectedItem();
				int endDay = Integer.valueOf(dayAsString);
				int endYear = Integer.valueOf(tpp.seasonEndYearField.getText());

				Long startDate = new GregorianCalendar(startYear, startMonth,
						startDay).getTimeInMillis();

				Long endDate = new GregorianCalendar(endYear, endMonth, endDay)
						.getTimeInMillis();

				int[] days = new int[trainingDays];
				int[] times = new int[trainingDays];

				for (int tdCounter = 0; tdCounter < trainingDays; tdCounter++) {

					vsp = (ValueStringPair) tpp.dayOfTrainingBox[tdCounter]
							.getSelectedItem();
					days[tdCounter] = vsp.getObjValue();

					vsp = (ValueStringPair) tpp.startOfTrainingBox[tdCounter]
							.getSelectedItem();
					times[tdCounter] = vsp.getObjValue();

				}

				model.createTrainingPlan(startDate, endDate, days, times);
				view.setMainContent(Commands.SHOW_EVENT_OVERVIEW);

				logger.warn("CONTROLLER: trainingPlanCreate - Create Plan");

			} catch (Exception e) {

				logger.error("CONTROLLER: trainingPlanCreate - Wrong Input");

				String messageContent = resBundle.getString("inputMissing");
				String messageTitle = resBundle.getString("attention");

				JOptionPane.showMessageDialog(null, messageContent,
						messageTitle, JOptionPane.ERROR_MESSAGE);
			}
		}
	}
}